/*
  Module        : {{ DUT.Module }}
  UMV Component : driver
  Author        : 
*/

`ifndef {{ DUT.Module | upper }}_DRIVER_SV
`define {{ DUT.Module | upper }}_DRIVER_SV

// --- UVM --- //
`include "uvm_macros.svh"
import uvm_pkg::*;

// --- Packages --- //
{% if DUT.Dependencies.Packages -%}
    {% for file, package in DUT.Dependencies.Packages.items() -%}
`include "{{ file }}"
import {{ package }}::*;
    {% endfor -%}
{% endif %}
// --- Includes --- //
`include "sequence_item.sv"
`include "interface.sv"
{% if DUT.Dependencies.Includes -%}
    {% for include in DUT.Dependencies.Includes -%}
`include "{{ include }}"
    {% endfor -%}
{% endif -%}
{# Blank Line #}
// --- Driver --- //
class {{ DUT.Module }}_driver extends uvm_driver#({{ DUT.Module }}_sequence_item);
  `uvm_component_utils({{ DUT.Module }}_driver)
  
  // --- Virtual Interface + Sequence Item --- //
  virtual {{ DUT.Module }}_if vif;
  {{ DUT.Module }}_sequence_item item;
  
  // --- Constructor --- //
  function new(string name = "{{ DUT.Module }}_driver", uvm_component parent);
    super.new(name, parent);
    `uvm_info("DRIVER_CLASS", "Inside Constructor", UVM_HIGH)
  endfunction : new
  
  // --- Build Phase --- //
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    `uvm_info("DRIVER_CLASS", "Build Phase", UVM_HIGH)
    
    // --- Virtual Interface Failure --- //
    if(!(uvm_config_db #(virtual {{ DUT.Module }}_if)::get(this, "*", "vif", vif))) begin
      `uvm_error("DRIVER_CLASS", "Failed to get virtual interface")
    end
    
  endfunction : build_phase
  
  // --- Run Phase --- //
  task run_phase (uvm_phase phase);
    super.run_phase(phase);
    `uvm_info("DRIVER_CLASS", "Inside Run Phase", UVM_HIGH)
    
    // --- Sequence Item Queue --- //
    forever begin
      item = {{ DUT.Module }}_sequence_item::type_id::create("item"); 
      seq_item_port.get_next_item(item);
      drive(item);
      seq_item_port.item_done();
    end
  endtask : run_phase
  
  // --- Drive Virtual Interface --- //
  task drive({{ DUT.Module }}_sequence_item item);
    {%- set ports = DUT.Ports.In %}

    {%- set max_len_in = ports | map('length') | max %}
    {%- set max_len_rst = DUT.Ports.Active_Low_Reset | length %}

    {%- set max_len = [max_len_in, max_len_rst] | max %}
    
    {%- macro pad(variable, length) -%}
        {{- variable -}}{{ ' ' * (length - variable|length) }}
    {%- endmacro %}

    @(posedge vif.{{ DUT.Ports.Clock }});
    vif.{{ pad(DUT.Ports.Active_Low_Reset, max_len) }} <= item.{{ DUT.Ports.Active_Low_Reset }};
    {% for port in ports -%}
    vif.{{ pad(port, max_len) }} <= item.{{ port }};
    {% endfor %}
  endtask : drive
  
endclass : {{ DUT.Module }}_driver

`endif